import {
  CommandDialog,
  MeasureResultType,
  ParameterObjectType,
} from '@amcax/base';
import { CommandFormWrapper } from '@app-base/common/components/command-form';
import { PickInputForFocus } from '@app-base/common/components/command-form/auto-focus/pick-input';
import { CommandFormFooter } from '@app-base/common/components/command-form/footer';
import { InputWithFormat } from '@app-base/common/components/command-form/InputWithFormat';
import {
  pickedProductData,
  PickInputValueType,
} from '@app-base/common/components/pick-input/types';
import { useCommand } from '@app-base/common/hooks/useCommand';
import {
  ProductObjectDisplayType,
  useProductCommandDisplay,
} from '@app-base/common/hooks/useProductCommandDisplay';
import { MeasureDistanceObj } from '@app-base/common/utils/measure/Distance';
import { ctx2 } from '@app-base/module';
import { wsApi } from '@app-base/server';
import { Checkbox, Select, Table } from 'antd';
import { DefaultOptionType } from 'antd/es/select';
import i18n from 'i18next';
import { useEffect, useRef, useState } from 'react';
import { Vector3Tuple } from 'three-legacy';
import { getIntervalCheckColumns } from './columns';
import { getAssembleNodesByIds } from '../../utils/tree-funcs';
import './style.scss';
import { IAssemblyDocTreeNode } from '@amcax/base';
import { CommandLabeledItemWrapper } from '@app-base/common/components/command-form/item-wrapper';

type IntervalResult = {
  key: string;
  vdata: [Vector3Tuple, Vector3Tuple];
  objects: string[];
  distance: number;
};

export type IntervalTableData = {
  key: string;
  part1: IAssemblyDocTreeNode | undefined;
  index1: number | null;
  part2: IAssemblyDocTreeNode | undefined;
  index2: number | null;
  distance: number;
  isIgnored: boolean;
};

const noIntervalDisplayOptions: DefaultOptionType[] = [
  {
    label: '默认着色',
    value: ProductObjectDisplayType.Default,
  },
  {
    label: '隐藏状态',
    value: ProductObjectDisplayType.Hidden,
  },
  {
    label: '透明状态',
    value: ProductObjectDisplayType.Transparent,
  },
];

const checkRangeOptions: DefaultOptionType[] = [
  {
    label: '所选项',
    value: 'checkSelectedItems',
  },
  {
    label: '所选项和装配体的其他项',
    value: 'checkSelectedItemWithOthers',
  },
];

const Dialog = ({ title, onClose }) => {
  const [includeObjects, setIncludeObjects] = useState<pickedProductData[]>([]);
  const [minDistance, setMinDistance] = useState(10);

  const [noIntervalDisplay, setNoIntervalDisplay] = useState(
    ProductObjectDisplayType.Default,
  );

  const [checkRange, setCheckRange] = useState<
    'checkSelectedItems' | 'checkSelectedItemWithOthers'
  >('checkSelectedItems');

  const [ignoreHidden, setIgnoreHidden] = useState(true);
  const [ignoreEqualInterval, setIgnoreEqualInterval] = useState(false);
  const [productAsPart, setProductAsPart] = useState(false);
  const [hideNonResultObjects, setHideNonResultObjects] = useState(false);

  const [isLoading, setIsLoading] = useState(false);
  const [hasCalculated, setHasCalculated] = useState(false);

  const [intervalResult, setIntervalResult] = useState<IntervalResult[]>([]);

  const [tableData, setTableData] = useState<IntervalTableData[]>([]);

  useEffect(() => {
    if (
      productAsPart &&
      includeObjects.some((v) => v.assemblyInfo.parent == null)
    ) {
      setIncludeObjects([]);
    }
  }, [productAsPart]);

  const {
    updatePartObjectsMap,
    hideObject,
    showObject,
    restoreObjectDisplay,
    restoreObjectVisibility,
    setObjectOpacity,
    docTree,
  } = useProductCommandDisplay();

  const onSubmit = async () => {
    setHasCalculated(true);
    try {
      setIsLoading(true);

      const objects = includeObjects.some((v) => v.assemblyInfo.parent == null)
        ? docTree.children
            .filter((v) => v.type === 'part' || v.type === 'product')
            .map((v) => v.key)
        : includeObjects.map((v) => v.productId);

      const result: IntervalResult[] = await wsApi.intervalCheck({
        objects,
        minInterval: minDistance,
        checkRange,
        ignoreHidden,
        ignoreEqualInterval,
        treatAssemblyAsOneObject: productAsPart,
      });

      updatePartObjectsMap(includeObjects.map((o) => o.assemblyInfo));

      setIntervalResult(
        result.map((r) => ({
          key: r.objects.join('-'),
          ...r,
        })),
      );
    } finally {
      setIsLoading(false);
    }
  };

  const renders = useRef<Map<string, MeasureDistanceObj>>(new Map());
  const objectsInResult = useRef<string[]>([]);
  const [selectedResult, setSelectedResult] = useState<string[]>([]);

  const updateObjectsInResultDisplay = () => {
    if (intervalResult.length === 0) {
      return;
    }

    includeObjects.forEach((o) => {
      if (!objectsInResult.current.includes(o.productId)) {
        return;
      }
      if (noIntervalDisplay === ProductObjectDisplayType.Default) {
        setObjectOpacity(o.productId, 1);
        showObject(o.productId);
      }

      if (noIntervalDisplay === ProductObjectDisplayType.Hidden) {
        setObjectOpacity(o.productId, 1);
        setTimeout(() => {
          hideObject(o.productId);
        });
      }

      if (noIntervalDisplay === ProductObjectDisplayType.Transparent) {
        setObjectOpacity(o.productId, 0.6);
        restoreObjectVisibility(o.productId);
      }
    });
  };

  useEffect(updateObjectsInResultDisplay, [noIntervalDisplay]);

  useEffect(() => {
    const hides = new Set<string>();
    intervalResult.forEach((v) => v.objects.forEach((o) => hides.add(o)));
    objectsInResult.current = Array.from(hides);

    ctx2.viewer.requestRender();

    // 生成表格数据，零件1/2与实体索引
    setTableData(
      intervalResult.map((r) => {
        const [id1, index1] = r.objects[0].split(':s');
        const [id2, index2] = r.objects[1].split(':s');
        return {
          key: r.key,
          part1: includeObjects.find(
            (obj) => getAssembleNodesByIds([id1], obj.assemblyInfo)?.length > 0,
          )?.assemblyInfo,
          index1: index1 == null ? null : Number(index1),
          part2: includeObjects.find(
            (obj) => getAssembleNodesByIds([id2], obj.assemblyInfo)?.length > 0,
          )?.assemblyInfo,
          index2: index2 == null ? null : Number(index2),
          distance: r.distance,
          isIgnored: false,
        };
      }),
    );
    setSelectedResult(intervalResult.map((v) => v.key));

    renders.current.forEach((v) => {
      v.destroy();
    });

    updateObjectsInResultDisplay();

    return () => {
      restoreObjectDisplay();
      renders.current.forEach((r) => r.destroy());
      renders.current.clear();
    };
  }, [intervalResult]);

  // 选中与忽略挂钩，脱离点击选中
  useEffect(() => {
    setSelectedResult(tableData.filter((v) => !v.isIgnored).map((v) => v.key));
  }, [tableData]);

  useEffect(() => {
    if (hideNonResultObjects) {
      includeObjects.forEach((o) => {
        if (!objectsInResult.current.includes(o.productId)) {
          hideObject(o.productId);
        }
      });
    } else {
      includeObjects.forEach((o) => restoreObjectVisibility(o.productId));
    }
  }, [hideNonResultObjects, intervalResult]);

  useEffect(() => {
    return () => {
      renders.current.forEach((v) => {
        v.destroy();
      });

      objectsInResult.current.forEach((h) => restoreObjectVisibility(h));
    };
  }, []);

  useEffect(() => {
    intervalResult.forEach((r) => {
      if (!selectedResult.includes(r.key)) {
        renders.current.get(r.key)?.destroy();
        renders.current.delete(r.key);
      }
    });
    selectedResult.forEach((key) => {
      if (renders.current.has(key)) {
        return;
      }
      const target = intervalResult.find((r) => r.key === key);
      const obj = new MeasureDistanceObj(
        {
          type: MeasureResultType.MinDistance,
          value: target.distance,
          xValue: Math.abs(target.vdata[0][0] - target.vdata[1][0]),
          yValue: Math.abs(target.vdata[0][1] - target.vdata[1][1]),
          zValue: Math.abs(target.vdata[0][2] - target.vdata[1][2]),
          geometry: {
            pt1: target.vdata[0],
            pt2: target.vdata[1],
          },
        },
        'min',
        true,
      );
      ctx2.cadScene.assisGroup.add(obj);
      renders.current.set(key, obj);
    });
    ctx2.viewer.requestRender();
  }, [selectedResult]);

  useCommand('interval-check', {
    finishDraw: onSubmit,
    onClose,
  });

  const setTableRowIgnored = (key: string, ignored: boolean) => {
    setTableData((prev) =>
      prev.map((row) =>
        row.key === key ? { ...row, isIgnored: ignored } : row,
      ),
    );
  };

  return (
    <CommandDialog
      className='cmd-interference-check'
      title={title}
      onClose={onClose}
      placement='topLeft'
    >
      <CommandFormWrapper>
        <CommandLabeledItemWrapper label='所选的零部件'>
          <PickInputForFocus
            allowMarqueeSelect={false}
            allowPickType={
              productAsPart
                ? [PickInputValueType.Product]
                : [PickInputValueType.Product, PickInputValueType.FullProduct]
            }
            onPick={(v) => {
              setIncludeObjects(v);
            }}
            autoFocus
            placeholder='请选择零部件'
            value={includeObjects}
            isMulti
            highlightSelection={intervalResult.length === 0}
            allowPickInvisibleAssembleFromLeftPanel
            noDuplicateProduct
          />
        </CommandLabeledItemWrapper>
        <CommandLabeledItemWrapper label='最大间隙值'>
          <InputWithFormat
            className='w-full'
            value={minDistance}
            placeholder='输入间隙值'
            inputType={ParameterObjectType.Length}
            disableParameter
            onChange={(v) => setMinDistance(v as number)}
          />
        </CommandLabeledItemWrapper>
        <CommandLabeledItemWrapper label='检查间隙的范围'>
          <Select
            placeholder='检查间隙的范围'
            options={checkRangeOptions}
            value={checkRange}
            onChange={(val) => {
              setCheckRange(val);
            }}
          />
        </CommandLabeledItemWrapper>

        <Checkbox
          checked={ignoreHidden}
          onChange={(e) => {
            setIgnoreHidden(e.target.checked);
          }}
        >
          忽略隐藏的零部件
        </Checkbox>
        <Checkbox
          checked={ignoreEqualInterval}
          onChange={(e) => {
            setIgnoreEqualInterval(e.target.checked);
          }}
        >
          忽略与指定值相等的间隙
        </Checkbox>
        <Checkbox
          checked={productAsPart}
          onChange={(e) => {
            setProductAsPart(e.target.checked);
          }}
        >
          视子装配为零部件
        </Checkbox>
        <Checkbox
          checked={hideNonResultObjects}
          onChange={(e) => {
            setHideNonResultObjects(e.target.checked);
          }}
        >
          透明显示未达标的部件
        </Checkbox>
        <div className='text-sm mt-2'>达标部件显示状态</div>
        <Select
          placeholder='达标部件显示状态'
          options={noIntervalDisplayOptions}
          value={noIntervalDisplay}
          onChange={(val) => {
            setNoIntervalDisplay(val);
          }}
        />
        {hasCalculated ? (
          <>
            <div className='text-sm mt-2'>结果：</div>
            {hasCalculated ? (
              <Table
                className='max-w-[600px]'
                pagination={false}
                rowSelection={{
                  selectedRowKeys: selectedResult,
                  renderCell: () => null,
                  hideSelectAll: true,
                }}
                scroll={{ y: 300 }}
                dataSource={tableData}
                locale={{
                  emptyText: (
                    <div className='h-24 flex items-center justify-center'>
                      无缝隙
                    </div>
                  ),
                }}
                columns={getIntervalCheckColumns(setTableRowIgnored)}
              />
            ) : (
              <div className='min-w-[500px]'></div>
            )}
          </>
        ) : (
          <div className='min-w-[500px]'></div>
        )}
        <CommandFormFooter
          onSubmit={onSubmit}
          onClose={onClose}
          submitText='计算'
          isSubmitLoading={isLoading}
        />
      </CommandFormWrapper>
    </CommandDialog>
  );
};

export const cmdIntervalCheck = {
  id: 'CmdIntervalCheck',
  label: 'command.interval-check',
  icon: 'commands/product/interval-check',
  create: (label?: string, onClose?: () => void) => (
    <Dialog
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
